﻿//
// Copyright (c) 2024 BPer
//
#include <bperrtc/BPerPushH265.h>

#include <bperrtp/BPerRtpHeader.h>
#include <bperrtp/BPerRtpPacket.h>
#include <bperrtp/BPerRtpConstant.h>
#include <bperrtp/BPerRtpRawPayload.h>
#include <bperrtp/BPerRtpSTAPPayload.h>
#include <bperrtp/BPerRtpFUAPayload2.h>

#include <bperutil/sys/BPerLog.h>
#include <bperavutil/video/BPerMeta.h>

#include <bperrtc/BPerPush.h>
#include <bperrtc/BPerRtcRtcp.h>
#include <bperrtc/BPerRtcConnection.h>

#include <bperutil/BPerAvH265.h>

#if	BPer_Enable_H265_Encoding

int32_t bPer_h265_encodeVideo(BPerRtcSession *session, BPerPushH265Rtp *rtp,
		BPerRtpPacket *pkt) {
	int err = 0;

	bPer_init_buffer(&rtp->buf, bPer_get_rtpBuffer(rtp->videoRtpBuffer),
			kRtpPacketSize);
	if ((err = bPer_encode_rtpHeader(&rtp->buf, &pkt->header)) != BPer_Ok) {
		return bPer_error_wrap(err, "rtp header(%d) encode packet fail",
				pkt->payload_type);
	}
	if (pkt->payload_type == BPerRtpPacketPayloadTypeRaw) {
		err = bPer_encode_h264_raw(&rtp->buf, &rtp->videoRawData);
	} else if (pkt->payload_type == BPerRtpPacketPayloadTypeFUA2) {
		err = bPer_encode_h265_fua2(&rtp->buf, &rtp->videoFua2Data);

	} else if (pkt->payload_type == BPerRtpPacketPayloadTypeSTAP) {
		err = bPer_encode_h265_stap(&rtp->buf, &rtp->stapData);
		bPer_reset_h2645_stap(&rtp->stapData);
	}

	if (err != BPer_Ok) {
		return bPer_error_wrap(err, "rtp payload(%d) encode packet fail",
				pkt->payload_type);
	}
	if (pkt->header.padding_length > 0) {
		uint8_t padding = pkt->header.padding_length;
		if (!bPer_buffer_require(&rtp->buf, padding)) {
			return bPer_error_wrap(ERROR_RTC_RTP_MUXER,
					"padding requires %d bytes", padding);
		}
		bPer_memset(rtp->buf.head, padding, padding);
		bPer_buffer_skip(&rtp->buf, padding);
	}

	session->context.stats.on_pub_videoRtp(&session->context.stats.sendStats,pkt,&rtp->buf);
	return bPer_send_avpacket(session, pkt, &rtp->buf);

}

int32_t bPer_push_h265_package_stap_a(void *psession,
		BPerPushH265Rtp *rtp, BPerFrame *videoFrame) {
	int err = BPer_Ok;
	BPerRtcSession *session=(BPerRtcSession*)psession;
	bPer_reset_rtpPacket(&rtp->videoStapPacket);
	rtp->videoStapPacket.header.payload_type = session->h265PayloadType;
	rtp->videoStapPacket.header.ssrc = rtp->videoSsrc;
	rtp->videoStapPacket.frame_type = BPerFrameTypeVideo;
	rtp->videoStapPacket.nalu_type = (BPerAvcNaluType) kStapA;
	rtp->videoStapPacket.header.marker = BPerFalse;
	rtp->videoStapPacket.header.sequence = rtp->videoSeq++;
	rtp->videoStapPacket.header.timestamp = videoFrame->pts;

	rtp->videoStapPacket.payload_type = BPerRtpPacketPayloadTypeSTAP;

	BPerSample vps_sample;
	BPerSample sps_sample;
	BPerSample pps_sample;
	bPer_decodeMetaH265(videoFrame->payload, videoFrame->nb, &vps_sample,
			&sps_sample, &pps_sample);
	bPer_reset_h2645_stap(&rtp->stapData);
	uint8_t header = (uint8_t) sps_sample.bytes[0];
	rtp->stapData.nri = header;
	bPer_insert_BPerSampleVector(&rtp->stapData.nalus, &vps_sample);
	bPer_insert_BPerSampleVector(&rtp->stapData.nalus, &sps_sample);
	bPer_insert_BPerSampleVector(&rtp->stapData.nalus, &pps_sample);

	if ((err = bPer_h265_encodeVideo(session, rtp, &rtp->videoStapPacket))
			!= BPer_Ok) {
		return bPer_error_wrap(err, "encode packet");
	}
	return err;
}

int32_t bPer_h265_package_single_nalu2(BPerRtcSession *session,
		BPerPushH265Rtp *rtp, BPerFrame *videoFrame) {
	int32_t err = BPer_Ok;


	bPer_reset_rtpPacket(&rtp->videoRawPacket);
	rtp->videoRawPacket.header.payload_type = session->h265PayloadType;
	rtp->videoRawPacket.header.ssrc = rtp->videoSsrc;
	rtp->videoRawPacket.frame_type = BPerFrameTypeVideo;
	rtp->videoRawPacket.header.sequence = rtp->videoSeq++;
	rtp->videoRawPacket.header.timestamp = videoFrame->pts;
	rtp->videoRawPacket.header.marker = BPerTrue;
	rtp->videoRawPacket.payload_type = BPerRtpPacketPayloadTypeRaw;

	rtp->videoRawData.payload = rtp->videoBuf;
	rtp->videoRawData.nb = videoFrame->nb;
	bPer_memcpy(rtp->videoRawData.payload, videoFrame->payload,
			rtp->videoRawData.nb);
	if ((err = bPer_h265_encodeVideo(session, rtp, &rtp->videoRawPacket))
			!= BPer_Ok) {
		return bPer_error_wrap(err, "encode packet");
	}
	return err;
}
int32_t bPer_h265_package_single_nalu(BPerRtcSession *session,
		BPerPushH265Rtp *rtp, char *p, int32_t plen, int64_t timestamp) {

	int32_t err = BPer_Ok;

	bPer_reset_rtpPacket(&rtp->videoRawPacket);
	rtp->videoRawPacket.header.payload_type = session->h265PayloadType;
	rtp->videoRawPacket.header.ssrc = rtp->videoSsrc;
	rtp->videoRawPacket.frame_type = BPerFrameTypeVideo;
	rtp->videoRawPacket.header.sequence = rtp->videoSeq++;
	rtp->videoRawPacket.header.timestamp = timestamp;

	rtp->videoRawPacket.payload_type = BPerRtpPacketPayloadTypeRaw;
	rtp->videoRawData.payload = rtp->videoBuf;
	rtp->videoRawData.nb = plen;
	bPer_memcpy(rtp->videoRawData.payload, p, plen);
	if ((err = bPer_h265_encodeVideo(session, rtp, &rtp->videoRawPacket))
			!= BPer_Ok) {
		return bPer_error_wrap(err, "encode packet");
	}

	return err;
}
int32_t bPer_h265_package_fu_a(BPerRtcSession *session, BPerPushH265Rtp *rtp,
		BPerFrame *videoFrame, int32_t fu_payload_size) {
	int32_t err = BPer_Ok;
	int32_t plen = videoFrame->nb;
	uint8_t *pdata = videoFrame->payload;
	char *p = (char*) pdata + 2;
	int32_t nb_left = plen - 2;
	uint8_t header = pdata[0];
	uint8_t nal_type = BPER_HEVC_NALU_TYPE(header);

	int32_t num_of_packet = ((plen - 1) % fu_payload_size==0)?0:1  + (plen - 1) / fu_payload_size;
	for (int32_t i = 0; i < num_of_packet; ++i) {
		int32_t packet_size = bPer_min(nb_left, fu_payload_size);

		bPer_reset_rtpPacket(&rtp->videoFuaPacket);
		rtp->videoFuaPacket.header.payload_type = session->h265PayloadType;
		rtp->videoFuaPacket.header.ssrc = rtp->videoSsrc;
		rtp->videoFuaPacket.frame_type = BPerFrameTypeVideo;
		rtp->videoFuaPacket.header.sequence = rtp->videoSeq++;
		rtp->videoFuaPacket.header.timestamp = videoFrame->pts;
		rtp->videoFuaPacket.header.marker = (i == num_of_packet - 1) ? 1 : 0;

		rtp->videoFuaPacket.payload_type = BPerRtpPacketPayloadTypeFUA2;

		bPer_memset(&rtp->videoFua2Data, 0, sizeof(BPerFua2H265Data));
		rtp->videoFua2Data.nri = (BPerHevcNaluType) header;
		rtp->videoFua2Data.nalu_type = (BPerHevcNaluType) nal_type;
		rtp->videoFua2Data.start = (i == 0) ? 1 : 0;
		rtp->videoFua2Data.end = (i == (num_of_packet - 1)) ? 1 : 0;

		rtp->videoFua2Data.payload = rtp->videoBuf;
		rtp->videoFua2Data.nb = packet_size;
		bPer_memcpy(rtp->videoFua2Data.payload, p, packet_size);

		p += packet_size;
		nb_left -= packet_size;
#if BPer_Enable_TWCC
		if(i==0){
			rtp->rtpExtension.twcc.sn=rtp->twccSeq++ ;
			rtp->videoFuaPacket.header.extensions=&rtp->rtpExtension;
			session->context.twcc.insertLocal(&session->context.twcc.session,rtp->rtpExtension.twcc.sn);
		}
#endif
		if ((err = bPer_h265_encodeVideo(session, rtp, &rtp->videoFuaPacket))
				!= BPer_Ok) {
			return bPer_error_wrap(err, "encode packet");
		}
		rtp->videoFuaPacket.header.extensions=NULL;

	}

	return err;
}



int32_t bPer_push_h265_video(void *psession, BPerPushH265Rtp *rtp,
		BPerFrame *videoFrame) {
	int32_t err = BPer_Ok;
	BPerRtcSession *session=(BPerRtcSession*)psession;
	if (videoFrame->nb <= kRtpMaxPayloadSize) {
		if ((err = bPer_h265_package_single_nalu2(session, rtp, videoFrame))
				!= BPer_Ok) {
			return bPer_error_wrap(err, "package single nalu");
		}
		session->context.stats.sendStats.videoRtpPacketCount++;
	} else {
		if ((err = bPer_h265_package_fu_a(session, rtp, videoFrame,
				kRtpMaxPayloadSize)) != BPer_Ok) {
			return bPer_error_wrap(err, "package fu-a");
		}
	}
	session->context.stats.sendStats.frameCount++;
	return err;
}
void bPer_create_pushH265(BPerPushH265 *push,BPerRtpBuffer* videoRtpBuffer) {
	if (push == NULL)		return;
	BPerPushH265Rtp *rtp=(BPerPushH265Rtp*)bPer_calloc(sizeof(BPerPushH265Rtp),1);
	push->push=rtp;
	rtp->videoSsrc = 0;
	rtp->videoSeq = 0;

	rtp->videoRtpBuffer = videoRtpBuffer;

	rtp->videoBuf = (char*) bPer_calloc(kRtpPacketSize,1);
	bPer_memset(&rtp->stapData, 0, sizeof(BPerRtpSTAPData));
	bPer_create_stap(&rtp->stapData);
#if BPer_Enable_TWCC
	rtp->rtpExtension.has_ext=1;
	rtp->rtpExtension.twcc.has_twcc=1;
	rtp->rtpExtension.twcc.id=BPer_TWCC_ID;
#endif
	push->on_video =bPer_push_h265_video;

	push->on_spspps =bPer_push_h265_package_stap_a;
}
void bPer_destroy_pushH265(BPerPushH265 *push) {
	if (push == NULL || push->push == NULL)
		return;
	BPerPushH265Rtp *rtp=push->push;
	bPer_free(rtp->videoBuf);
	bPer_reset_h2645_stap(&rtp->stapData);
	bPer_destroy_stap(&rtp->stapData);
	bPer_destroy_rtpPacket(&rtp->videoFuaPacket);
	bPer_destroy_rtpPacket(&rtp->videoRawPacket);
	bPer_destroy_rtpPacket(&rtp->videoStapPacket);

	bPer_free(push->push);
}

#endif
